#include <sysdep.h>
#include <strutl.h>
#ifndef WIN32
/* charset convert */
#include <iconv.h>
#endif
/* soft_conf_dir */
#include <core.h>

#ifdef WIN32
int gbk2utf8(unsigned char * gdkstr,unsigned char * utf8str,int utf8len)
{
	wchar_t * lpUnicodeStr = NULL;
	int nRetLen = 0;
	
	if(!gdkstr)
		return 0;
	
	/* get Unicode length */
	nRetLen = MultiByteToWideChar(CP_ACP, 0, (char *)gdkstr, -1, NULL, 0);
	lpUnicodeStr = malloc(sizeof(WCHAR) * (nRetLen + 1));
	
	/* transform to Unicode */
	nRetLen = MultiByteToWideChar(CP_ACP, 0, (char *)gdkstr, -1, lpUnicodeStr, nRetLen);  
	if(!nRetLen)
		return 0;
	/* get UTF-8 length */
	nRetLen = WideCharToMultiByte(CP_UTF8, 0, lpUnicodeStr, -1, NULL, 0, NULL, NULL);  
	
	/* if buffer is NULL, then output require length */
	if(!utf8str) {
		if(lpUnicodeStr)
			free(lpUnicodeStr);
		return nRetLen;
	}
	
	if(utf8len < nRetLen) {
		if(lpUnicodeStr)
			free(lpUnicodeStr);
		return 0;
	}
	/* transform to UTF-8 */
	nRetLen = WideCharToMultiByte(CP_UTF8, 0, lpUnicodeStr, -1, (char *)utf8str, utf8len, NULL, NULL);
	
	if(lpUnicodeStr)
		free(lpUnicodeStr);
	
	return nRetLen;
}

int utf82gbk(unsigned char *lpUTF8Str, unsigned char *lpGBKStr,int nGBKStrLen)
{
	wchar_t *lpUnicodeStr = NULL;
	int nRetLen = 0;
	
	if(!lpUTF8Str)
		return 0;
	
	nRetLen = MultiByteToWideChar(CP_UTF8, 0, (char *)lpUTF8Str, -1, NULL, 0);
	
	lpUnicodeStr = malloc(sizeof(WCHAR) * (nRetLen + 1));
	if (!lpUnicodeStr)
		return 0;
	
	nRetLen = MultiByteToWideChar(CP_UTF8, 0, (char *)lpUTF8Str, -1, lpUnicodeStr, nRetLen);
	
	if(!nRetLen)
		return 0;
	
	nRetLen = WideCharToMultiByte(CP_ACP, 0, lpUnicodeStr, -1, NULL, 0, NULL, 0);
	
	if(!lpGBKStr) {
		if(lpUnicodeStr)
			free(lpUnicodeStr);
		return nRetLen;
	}
	
	if(nGBKStrLen < nRetLen) {
		if(lpUnicodeStr)
			free(lpUnicodeStr);
		return 0;
	}
	
	nRetLen = WideCharToMultiByte(CP_ACP,0,lpUnicodeStr,-1,(char *)lpGBKStr,nRetLen,NULL,NULL);  //ת????GBK????
	
	if(lpUnicodeStr)
		free(lpUnicodeStr);
	return nRetLen;
}
#else
int gbk2utf8(unsigned char *in,unsigned char *out,int outlen)
{
	return code_convert("gb2312", "utf-8", in, 1024, out, outlen);
}
int utf82gbk(unsigned char *in, unsigned char *out,int outlen)
{
	return code_convert("utf-8", "gb2312", in, 1024, out, outlen);
}

/* copy from internet shamelessly. Only for test.
 * In this way, need set term locale to gbk. 
 *
 * TODO: dynamic adjust encode for several term or else output module.
 *
 * @param - from_charset: charset from
 * @param - to_charset: charset to
 * @param - inbuf / inlen:
 * @param - outbuf / outlen:
 *
 */
int code_convert(char *from_charset, char *to_charset, 
		 char *inbuf, int inlen, 
		 char *outbuf, int outlen)
{
	iconv_t cd;
	int rc;
	char **pin = &inbuf;
	char **pout = &outbuf;

	cd = iconv_open(to_charset, from_charset);
	if (cd == 0) 
		return -1;

	memset(outbuf, 0, outlen);
	if (iconv(cd, pin, &inlen, pout, &outlen) == -1) 
		return -1;
	iconv_close(cd);
	return 0;
}
#endif

int file_get_line(int fd, char *buf)
{
	int res, i = 0;
	do {
		res = read(fd, &buf[i], 1);
		if (buf[i] == '\n')
			break;
		if (res < 1)
			return -1;
		i++;
	} while (1);
	/* delete '\n' */
	buf[i] = '\0';
	return 0;
}

/* convert a string to an integer */
int str2int(const string_map_t *table, const char *name, int def)
{
	const string_map_t *this;

	for (this = table; name && this->name != NULL; this++) {
		if (strcasecmp(this->name, name) == 0) {
			return this->number;
		}
	}
	return def;
}

/* convert an integer to a string */
const char *int2str(const string_map_t *table, int number, const char *def)
{
	const string_map_t *this;

	for (this = table; this->name != NULL; this++) {
		if (this->number == number) {
			return this->name;
		}
	}

	return def;
}

static int ishex(const char ch)
{
	return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
		(ch >= 'A' && ch <= 'F');
}

const char *u16tostr(uint16_t port)
{
	static char buf[7];
	sprintf(buf, "0x%04X", port);
	return buf;
}

const char *u8tostr(uint16_t num)
{
	static char buf[5];
	sprintf(buf, "0x%02X", num);
	return buf;
}

uint8_t hexchar(char h)
{
	if (h >= 'a' && h <= 'f')
		return h - 'a' + 10;
	else if (h >= 'A' && h <= 'F')
		return h - 'A' + 10;
	else if (h >= '0' && h <= '9')
		return h - '0';
	else
		return -1;
}

int hex2i(const char *hex)
{
	int total = 0, h, i;
	for (i = 0; hex[i]; i++) {
		h = hexchar(hex[i]);
		total *= 16;
		total += h;
	}
	return total;
}

static int oct2i(const char *oct)
{
	int total = 0, h, i;
	for (i = 0; oct[i]; i++) {
		h = (oct[i] - '0');
		total *= 8;
		total += h;
	}
	return total;
}

uint16_t str2u16(const char *str)
{
	if (str) {
		if (str[0] == '0') {
			if (str[1] == 'x')
				return hex2i(str+2);
			else
				return oct2i(str+1);
		}
		return atoi(str);
	}
	return 0;
}

uint8_t str2u8(const char *str)
{
	if (str) {
		if (str[0] == '0') {
			if (str[1] == 'x')
				return hex2i(str+2);
			else
				return oct2i(str+1);
		}
		return atoi(str);
	}
	return 0;
}

void chomp(char *line)
{
	char *last;
	
	for (last = line + strlen(line); last >= line; last--) {
		if (strchr(" \r\n\t", *last))
			*last = '\0';
		else
			break;
	}
}

char *time_string(int utc, time_t tm)
{
	struct tm *ptm;
	static char tmstr[32] = "";

	if (utc)
		ptm = gmtime(&tm);
	else
		ptm = localtime(&tm);
	
	if (ptm)
		strftime(tmstr, sizeof(tmstr), "%Y%m%d %H:%M:%S", ptm);
	else
		sprintf(tmstr, "Invalid Time %d", (uint32_t)tm);
	return tmstr;
}

/* like strncpy, but always adds \0 */
size_t strlcpy(char *dst, const char *src, size_t size)
{
	size_t bytes = 0;
	char *q = dst;
	const char *p = src;
	char ch;

	while ((ch = *p++)) {
		if (bytes+1 < size)
			*q++ = ch;
		bytes++;
	}

	/* If size == 0 there is no space for a final null... */
	if (size)
		*q = '\0';
	return bytes;
}

size_t strlcat(char *dst, const char *src, size_t size)
{
	size_t bytes = 0;
	char *q = dst;
	const char *p = src;
	char ch;

	while (bytes < size && *q) {
		q++;
		bytes++;
	}

	if (bytes == size)
		return (bytes + strlen(src));

	while ((ch = *p++)) {
		if (bytes + 1 < size)
			*q++ = ch;
		bytes++;
	}
	*q = '\0';
	return bytes;
}

struct buffer_info {
	char *ptr;
	int len;
};

/*
 * slprintf - format a message into a buffer.  Like sprintf except we
 * also specify the length of the output buffer, and we handle
 * %m (error message), %v (visible string),
 * %q (quoted string), %t (current time) and %I (IP address) formats.
 * Doesn't do floating-point formats.
 * Returns the number of chars put into buf.
 */
int slprintf(char *buf, int buflen, const char *fmt, ...)
{
	va_list args;
	int n;
	
	va_start(args, fmt);
	n = vslprintf(buf, buflen, fmt, args);
	va_end(args);
	return n;
}

/*
 * vslprintf - like slprintf, takes a va_list instead of a list of args.
 */
#define OUTCHAR(c)	(buflen > 0? (--buflen, *buf++ = (c)): 0)

int vslprintf(char *buf, int buflen, const char *fmt, va_list args)
{
	int c, i, n;
	int width, prec, fillch;
	int base, len, neg, quoted;
	unsigned long val = 0;
	char *str, *buf0;
	const char *f;
	unsigned char *p;
	char num[32];
	time_t t;
	uint32_t ip;
	static char hexchars[] = "0123456789abcdef";
	
	buf0 = buf;
	--buflen;
	while (buflen > 0) {
		for (f = fmt; *f != '%' && *f != 0; ++f)
			;
		if (f > fmt) {
			len = f - fmt;
			if (len > buflen)
				len = buflen;
			memcpy(buf, fmt, len);
			buf += len;
			buflen -= len;
			fmt = f;
		}
		if (*fmt == 0)
			break;
		c = *++fmt;
		width = 0;
		prec = -1;
		fillch = ' ';
		if (c == '0') {
			fillch = '0';
			c = *++fmt;
		}
		if (c == '*') {
			width = va_arg(args, int);
			c = *++fmt;
		} else {
			while (isdigit(c)) {
				width = width * 10 + c - '0';
				c = *++fmt;
			}
		}
		if (c == '.') {
			c = *++fmt;
			if (c == '*') {
				prec = va_arg(args, int);
				c = *++fmt;
			} else {
				prec = 0;
				while (isdigit(c)) {
					prec = prec * 10 + c - '0';
					c = *++fmt;
				}
			}
		}
		str = 0;
		base = 0;
		neg = 0;
		++fmt;
		switch (c) {
		case 'l':
			c = *fmt++;
			switch (c) {
			case 'd':
				val = va_arg(args, long);
				if (val < 0) {
					neg = 1;
					val = (unsigned)(-(signed)val);
				}
				base = 10;
				break;
			case 'u':
				val = va_arg(args, unsigned long);
				base = 10;
				break;
			default:
				*buf++ = '%'; --buflen;
				*buf++ = 'l'; --buflen;
				--fmt;		/* so %lz outputs %lz etc. */
				continue;
			}
			break;
			case 'd':
				i = va_arg(args, int);
				if (i < 0) {
					neg = 1;
					val = -i;
				} else
					val = i;
				base = 10;
				break;
			case 'u':
				val = va_arg(args, unsigned int);
				base = 10;
				break;
			case 'o':
				val = va_arg(args, unsigned int);
				base = 8;
				break;
			case 'x':
			case 'X':
				val = va_arg(args, unsigned int);
				base = 16;
				break;
			case 'p':
				val = (unsigned long) va_arg(args, void *);
				base = 16;
				neg = 2;
				break;
			case 's':
				str = va_arg(args, char *);
				break;
			case 'c':
				num[0] = va_arg(args, int);
				num[1] = 0;
				str = num;
				break;
			case 'm':
				str = strerror(errno);
				break;
			case 'I':
				ip = va_arg(args, uint32_t);
				ip = ntohl(ip);
				slprintf(num, sizeof(num), "%d.%d.%d.%d",
					 (uint32_t)((ip >> 24) & 0xff),
					 (uint32_t)((ip >> 16) & 0xff),
					 (uint32_t)((ip >> 8) & 0xff),
					 (uint32_t)(ip & 0xff));
				str = num;
				break;
#if 0	/* not used, and breaks on S/390, apparently */
			case 'r':
				f = va_arg(args, char *);
				n = vslprintf(buf, buflen + 1, f, va_arg(args, va_list));
				buf += n;
				buflen -= n;
				continue;
#endif
			case 't':
				time(&t);
				str = ctime(&t);
				str += 4;		/* chop off the day name */
				str[15] = 0;	/* chop off year and newline */
				break;
			case 'v':		/* "visible" string */
			case 'q':		/* quoted string */
				quoted = c == 'q';
				p = va_arg(args, unsigned char *);
				if (fillch == '0' && prec >= 0) {
					n = prec;
				} else {
					n = strlen((char *)p);
					if (prec >= 0 && n > prec)
						n = prec;
				}
				while (n > 0 && buflen > 0) {
					c = *p++;
					--n;
					if (!quoted && c >= 0x80) {
						OUTCHAR('M');
						OUTCHAR('-');
						c -= 0x80;
					}
					if (quoted && (c == '"' || c == '\\'))
						OUTCHAR('\\');
					if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
						if (quoted) {
							OUTCHAR('\\');
							switch (c) {
							case '\t':	OUTCHAR('t');	break;
							case '\n':	OUTCHAR('n');	break;
							case '\b':	OUTCHAR('b');	break;
							case '\f':	OUTCHAR('f');	break;
							default:
								OUTCHAR('x');
								OUTCHAR(hexchars[c >> 4]);
								OUTCHAR(hexchars[c & 0xf]);
							}
						} else {
							if (c == '\t')
								OUTCHAR(c);
							else {
								OUTCHAR('^');
								OUTCHAR(c ^ 0x40);
							}
						}
					} else
						OUTCHAR(c);
				}
				continue;
			case 'B':
				p = va_arg(args, unsigned char *);
				for (n = prec; n > 0; --n) {
					c = *p++;
					if (fillch == ' ')
						OUTCHAR(' ');
					OUTCHAR(hexchars[(c >> 4) & 0xf]);
					OUTCHAR(hexchars[c & 0xf]);
				}
				continue;
			default:
				*buf++ = '%';
				if (c != '%')
					--fmt;		/* so %z outputs %z etc. */
				--buflen;
				continue;
		}
		if (base != 0) {
			str = num + sizeof(num);
			*--str = 0;
			while (str > num + neg) {
				*--str = hexchars[val % base];
				val = val / base;
				if (--prec <= 0 && val == 0)
					break;
			}
			switch (neg) {
			case 1:
				*--str = '-';
				break;
			case 2:
				*--str = 'x';
				*--str = '0';
				break;
			}
			len = num + sizeof(num) - 1 - str;
		} else {
			len = strlen(str);
			if (prec >= 0 && len > prec)
				len = prec;
		}
		if (width > 0) {
			if (width > buflen)
				width = buflen;
			if ((n = width - len) > 0) {
				buflen -= n;
				for (; n > 0; --n)
					*buf++ = fillch;
			}
		}
		if (len > buflen)
			len = buflen;
		memcpy(buf, str, len);
		buf += len;
		buflen -= len;
	}
	*buf = 0;
	return buf - buf0;
}

void daemonize(void)
{
#ifndef WIN32
        pid_t pid;

        pid = fork();
        if (pid < 0) exit(1);         /* fork failed */
        if (pid > 0) _exit(0);        /* parent exits */

        setsid();

        pid = fork();
        if (pid < 0) exit(1);         /* fork failed */
        if (pid > 0) _exit(0);        /* parent exits */

        if (chdir("/") < 0) {
		perror("chdir");
		exit(1);
	}

        (void) freopen("/dev/null", "r", stdin);
        (void) freopen("/dev/null", "w", stdout);
        (void) freopen("/dev/null", "w", stderr);
#endif
}

#ifdef WIN32
uint64_t strtoull(const char *cp,char **endp,unsigned int base)
{
	uint64_t result = 0,value;

	if (!base) {
		base = 10;
		if (*cp == '0') {
			base = 8;
			cp++;
			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
				cp++;
				base = 16;
			}
		}
	} else if (base == 16) {
		if (cp[0] == '0' && toupper(cp[1]) == 'X')
			cp += 2;
	}
	while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
	    ? toupper(*cp) : *cp)-'A'+10) < base) {
		result = result*base + value;
		cp++;
	}
	if (endp)
		*endp = (char *)cp;
	return result;
}
#endif


int string_is_true(const char *str)
{
	if (strcasecmp(str, "true") == 0)
		return 1;
	if (strcasecmp(str, "yes") == 0)
		return 1;
	if (strcasecmp(str, "1") == 0)
		return 1;
	return 0;
}

void remove_trailing_chars(char *path, char c)
{
	size_t len;

	len = strlen(path);
	while (len > 0 && path[len-1] == c)
		path[--len] = '\0';
}

size_t path_encode(char *s, size_t len)
{
	char *t;
	size_t i, j;

	t = malloc((len * 3)+1);
	if (!t) return 0;

	t[0] = '\0';
	for (i = 0, j = 0; s[i] != '\0'; i++) {
		if (s[i] == '/') {
			memcpy(&t[j], "\\x2f", 4);
			j += 4;
		} else if (s[i] == '\\') {
			memcpy(&t[j], "\\x5c", 4);
			j += 4;
		} else {
			t[j] = s[i];
			j++;
		}
	}
	t[j] = '\0';
	strncpy(s, t, len);
	if (t) free(t);
	return j;
}

size_t path_decode(char *s)
{
	size_t i, j;

	for (i = 0, j = 0; s[i] != '\0'; j++) {
		if (memcmp(&s[i], "\\x2f", 4) == 0) {
			s[j] = '/';
			i += 4;
		}else if (memcmp(&s[i], "\\x5c", 4) == 0) {
			s[j] = '\\';
			i += 4;
		} else {
			s[j] = s[i];
			i++;
		}
	}
	s[j] = '\0';
	return j;
}

/* count of characters used to encode one unicode char */
static int utf8_encoded_expected_len(const char *str)
{
	unsigned char c = (unsigned char)str[0];

	if (c < 0x80)
		return 1;
	if ((c & 0xe0) == 0xc0)
		return 2;
	if ((c & 0xf0) == 0xe0)
		return 3;
	if ((c & 0xf8) == 0xf0)
		return 4;
	if ((c & 0xfc) == 0xf8)
		return 5;
	if ((c & 0xfe) == 0xfc)
		return 6;
	return 0;
}

/* decode one unicode char */
static int utf8_encoded_to_unichar(const char *str)
{
	int unichar;
	int len;
	int i;

	len = utf8_encoded_expected_len(str);
	switch (len) {
	case 1:
		return (int)str[0];
	case 2:
		unichar = str[0] & 0x1f;
		break;
	case 3:
		unichar = (int)str[0] & 0x0f;
		break;
	case 4:
		unichar = (int)str[0] & 0x07;
		break;
	case 5:
		unichar = (int)str[0] & 0x03;
		break;
	case 6:
		unichar = (int)str[0] & 0x01;
		break;
	default:
		return -1;
	}

	for (i = 1; i < len; i++) {
		if (((int)str[i] & 0xc0) != 0x80)
			return -1;
		unichar <<= 6;
		unichar |= (int)str[i] & 0x3f;
	}

	return unichar;
}

/* expected size used to encode one unicode char */
static int utf8_unichar_to_encoded_len(int unichar)
{
	if (unichar < 0x80)
		return 1;
	if (unichar < 0x800)
		return 2;
	if (unichar < 0x10000)
		return 3;
	if (unichar < 0x200000)
		return 4;
	if (unichar < 0x4000000)
		return 5;
	return 6;
}

/* check if unicode char has a valid numeric range */
static int utf8_unichar_valid_range(int unichar)
{
	if (unichar > 0x10ffff)
		return 0;
	if ((unichar & 0xfffff800) == 0xd800)
		return 0;
	if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
		return 0;
	if ((unichar & 0xffff) == 0xffff)
		return 0;
	return 1;
}

/* validate one encoded unicode char and return its length */
int utf8_encoded_valid_unichar(const char *str)
{
	int len;
	int unichar;
	int i;

	len = utf8_encoded_expected_len(str);
	if (len == 0)
		return -1;

	/* ascii is valid */
	if (len == 1)
		return 1;

	/* check if expected encoded chars are available */
	for (i = 0; i < len; i++)
		if ((str[i] & 0x80) != 0x80)
			return -1;

	unichar = utf8_encoded_to_unichar(str);

	/* check if encoded length matches encoded value */
	if (utf8_unichar_to_encoded_len(unichar) != len)
		return -1;

	/* check if value has valid range */
	if (!utf8_unichar_valid_range(unichar))
		return -1;

	return len;
}

/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
int replace_chars(char *str, const char *white)
{
	size_t i = 0;
	int replaced = 0;

	while (str[i] != '\0') {
		int len;

		/* accept whitelist */
		if (white != NULL && strchr(white, str[i]) != NULL) {
			i++;
			continue;
		}

		/* accept plain ascii char */
		if ((str[i] >= '0' && str[i] <= '9') ||
		    (str[i] >= 'A' && str[i] <= 'Z') ||
		    (str[i] >= 'a' && str[i] <= 'z')) {
			i++;
			continue;
		}

		/* accept hex encoding */
		if (str[i] == '\\' && str[i+1] == 'x') {
			i += 2;
			continue;
		}

		/* accept valid utf8 */
		len = utf8_encoded_valid_unichar(&str[i]);
		if (len > 1) {
			i += len;
			continue;
		}

		/* if space is allowed, replace whitespace with ordinary space */
		if (isspace(str[i]) && strchr(white, ' ') != NULL) {
			str[i] = ' ';
			i++;
			replaced++;
			continue;
		}

		/* everything else is replaced with '_' */
		str[i] = '_';
		i++;
		replaced++;
	}

	return replaced;
}

size_t strnlen(const char *s, size_t count)
{
	const char *sc;

	for (sc = s; count-- && *sc != '\0'; ++sc)
		/* nothing */;
	return sc - s;
}

void do_percentm(char *obuf, const char *ibuf)
{
	const char *s = ibuf;
	char *p = obuf;
	int infmt = 0;
	const char *m;
	int len = 0;

#define CVT_BUF_MAX 1023
	while (*s) {
		if (infmt) {
			if (*s == 'm') {
				m = strerror (errno);
				if (!m)
					m = "<unknown error>";
				len += strlen (m);
				if (len > CVT_BUF_MAX)
					goto out;
				strcpy (p - 1, m);
				p += strlen (p);
				++s;
			} else {
				if (++len > CVT_BUF_MAX)
					goto out;
				*p++ = *s++;
			}
			infmt = 0;
		} else {
			if (*s == '%')
				infmt = 1;
			if (++len > CVT_BUF_MAX)
				goto out;
			*p++ = *s++;
		}
	}
      out:
	*p = 0;
}

void strupper(char *string)
{
	for (; string && *string; string++) {
		if (islower(*string))
			*string = _toupper(*string);
	}
}

char *strrcat(char *old_str, const char *str)
{
	int old_size;
	int new_size;
	char *text;
	int size = strlen(str);
	
	if (old_str) {
		old_size = strlen(old_str);
		new_size = old_size + size + 1;
		text = realloc(old_str, new_size);
		memcpy(text + old_size, str, size);
		text[new_size-1] = 0;
	} else {
		text = strdup(str);
	}
	return text;
}

/* 0101 -> 1010, n is 4 */
int reverse_bits(int orig_bits, int n)
{
	int i, res = 0;
	for(i = 0; i < n; i++) {
		res = (res << 1) |(orig_bits & 1);
		orig_bits >>= 1;
	} 
	return res;
}

char *str_convert_char(char *s, char from, char to)
{
	char *p;
	for(p = s; *p; p++)
		if(*p == from)
			*p = to; 
	return s;
}
